package com.hkd.friend.service.user.Impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hkd.common.core.constants.CacheConstants;
import com.hkd.common.core.constants.HttpConstants;
import com.hkd.common.core.domain.LoginUser;
import com.hkd.common.core.domain.R;
import com.hkd.common.core.domain.vo.LoginUserVo;
import com.hkd.common.core.enums.ResultCode;
import com.hkd.common.core.enums.UserIdentity;
import com.hkd.common.core.enums.UserStatus;
import com.hkd.common.redis.service.RedisService;
import com.hkd.common.security.service.TokenService;
import com.hkd.friend.domain.user.User;
import com.hkd.friend.domain.user.dto.UserDTO;
import com.hkd.friend.mapper.user.UserMapper;
import com.hkd.friend.service.user.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private RedisService redisService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UserMapper userMapper;

    @Value("${jwt.secret}")
    private String secret;

    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration;

    @Value("${sms.send-limit:3}")
    private Long sendLimit;
    @Override
    public R sendCode(UserDTO userDTO) {
        //手机号合法性校验
        if(!checkPhone(userDTO.getPhone())){
            log.error("手机号不合法");
            return R.failed(ResultCode.FAILED_USER_PHONE);
        }
        String phoneCodeKey = getPhoneCodeKey(userDTO.getPhone());
        Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.MINUTES);
        //验证码发送频率限制
        Long expireTime = (phoneCodeExpiration - expire)*60;
        if (expire != null && (expireTime) <= 60) {
             log.error("验证码发送频率过高，请稍后再试");
             return R.failed(ResultCode.FAILED_SMS_FREQUENCY);
        }
        //每天限制50次
        String codeTimeKey = getCodeTimeKey(userDTO.getPhone());
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        if (sendTimes != null && sendTimes >= sendLimit) {
            log.error("验证码发送超出当日限制");
            return R.failed(ResultCode.FAILED_SMS_TIME_LIMIT);
        }

        String code = RandomUtil.randomNumbers(6);
        //redis缓存验证码 p:c:手机号  code验证码
        redisService.setCacheObject(phoneCodeKey,code,phoneCodeExpiration, TimeUnit.MINUTES);
        //次数计数加一
        redisService.increment(codeTimeKey);
        if (sendTimes == null) {  //说明是当天第一次发起获取验证码的请求
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }

        //TODO 发送验证码
        boolean ret = true;
        if (!ret) {
            log.error("发送验证码失败");
            return R.failed(ResultCode.FAILED_SEND_SMS);
        }
        //用不了第三方短信服务，暂时用log打印验证码
        return R.success(code);
    }

    @Override
    public R codeLogin(UserDTO userDTO) {
        //1.校验新老用户
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, userDTO.getPhone()));

        //2.验证码校验
        String phoneCodeKey = getPhoneCodeKey(userDTO.getPhone());
        String cacheCode = redisService.getCacheObject(phoneCodeKey, String.class);
        //验证码校验是否过期
        if(StrUtil.isEmpty(cacheCode)){
            log.error("验证码已过期");
            return R.failed(ResultCode.FAILED_CODE_EXPIRED);
        }
        //验证码校验是否正确
        if (!cacheCode.equals(userDTO.getCode())) {
            log.error("验证码错误");
            return R.failed(ResultCode.FAILED_CODE_ERROR);
        }

        //3.注册新用户
        if (user == null) {
            //新用户，注册
            user = new User();
            user.setPhone(userDTO.getPhone());
            user.setStatus(UserStatus.Normal.getValue());
            int insert = userMapper.insert(user);
            if (insert != 1) {
                log.error("注册失败");
                return R.failed(ResultCode.FAILED_REGISTER_USER);
            }
        }

        //4.登录成功，缓存用户信息Token
        redisService.deleteObject(phoneCodeKey);
        String token = tokenService.createToken(user.getUserId(), secret, UserIdentity.ORDINARY.getValue(), user.getNickName(),user.getHeadImage());
        return R.success(token);
    }

    @Override
    public R logout(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        //1、解析token
        Boolean result = tokenService.deleteLoginUser(token, secret);
        if(!result ){
            log.warn(ResultCode.FAILED_DELETE.getMessage());
            return R.failed(ResultCode.FAILED_DELETE.getCode(),ResultCode.FAILED_DELETE.getMessage());
        }
        return R.success();
    }

    @Override
    public R getInfo(String token) {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        //1、解析token
        LoginUser loginUser = tokenService.getLoginUser(token, secret);

        //2、校验token
        if(loginUser == null){
            log.warn(ResultCode.FAILED_TOKEN_GET_INFO.getMessage());
            return R.failed(ResultCode.FAILED_TOKEN_GET_INFO);
        }
        //3、查询用户信息
        LoginUserVo loginUserVo = new LoginUserVo();
        loginUserVo.setNickName(loginUser.getNickName());
        loginUserVo.setHeadImage(loginUser.getHeadImage());

        Date date = new Date();
        SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");

        //4、返回用户信息
        return R.success(loginUserVo,"您好！ "+loginUser.getNickName()+" 北京时间:"+timeFormat.format(date));
    }

    private String getPhoneCodeKey(String phone) {
        return CacheConstants.PHONE_CODE_KEY + phone;
    }
    private String getCodeTimeKey(String phone) {
        return CacheConstants.CODE_TIME_KEY + phone;
    }

    public static boolean checkPhone(String phone) {
        Pattern regex = Pattern.compile("^1[2|3|4|5|6|7|8|9][0-9]\\d{8}$");
        Matcher m = regex.matcher(phone);
        return m.matches();
    }
}
